The Card Controller (attached to the Gameboard node) exposes all the card features available to creators.

The Gameboard node is always available in your game as long as the SDK has been integrated. In order to access it we just need to find it.

    var gameboard = GetNode("/root/GameboardSDK") as GameboardPlugin;

With access to our Gameboard node we can then move on to obtain a reference to the Card Controller. This is the controller that handles all the functions related to cards.

We will first need to define a reference to the controller to make it available throughout our script:

     CardController cardController;

With our listener defined, we can now proceed to listen for card events. In our case we'll have functions called OnCardPlayed, OnCardTapped, OnCardsReorderedEvent to handle these events.

    cardController = gameboard.GetNode("CardController") as CardController;

    cardController.CardPlayed += OnCardPlayed;
    cardController.CardTapped += OnCardTapped;
    cardController.CardsReordered += OnCardsReorderedEvent;

Now that we are able to receive the card events all that remains is to handle them in the game.

    void OnCardPlayed(CompanionCardPlayedEventArgs cardPlayedEvent)
    {
        // Handle Card Played
    }

    void CardTapped(CompanionCardPlayedEventArgs cardTappedEvent) //Same object/info as cardPlayed, except the card was only tapped instead of played
    {
        // Handle Card Tapped
    }

    void OnCardsReorderedEvent(GameboardCardsReorderedEventArgs cardsReorderedEvent)
    {
        //Handler cards reordered
    }

The companion comes with 3 card template types that have their own style and layout:

Card is the default template, which shows 3 cards at a time with default arrow assets.

Step-by-Step

Tile shows 7 ‘cards' at a time, but they are smaller and have a 1:1 size ratio.

Step-by-Step

Playing shows the cards in a fanned view.

Step-by-Step

You should set whatever template you would like at the start of play, because the card sized added to the hand will be determined based on the current template.

    cardController.SetCardTemplateType("userABC", CompanionCardTemplateType.Card);
    cardController.SetCardTemplateType("userABC", CompanionCardTemplateType.Tile);
    cardController.SetCardTemplateType("userABC", CompanionCardTemplateType.Playing);

With the controller at hand we can also send requests to Companion.

The Card Controller also allows sending card request to users. The following calls are supported:

In this example we'll request a card to be created on Companion for a user whose ID user ID is "userABC". This card will be available on Companion under the ID "cardIdA" and reference an asset with ID "frontTextureId" for the front of the card and an asset with ID "backCardTextureUid" for the back of the card.

    // Create a hand on user with ID userAB
    CompanionCreateObjectEventArgs handCreationEvent = await cardController.CreateCompanionCard("userABC", "cardIdA", "frontTextureId", "backCardTextureUid");

If you want to have a base card asset, and generate text on top of that, you can now send text on card creation, or set card text after the fact. Note that this is only currently support for the default Card template, and the Playing template.

Create Card with Text

In this example we'll request a card to be created on Companion for a user whose ID user ID is "userABC". This card will be available on Companion under the ID "cardIdA" and reference an asset with ID "frontTextureId" for the front of the card and an asset with ID "backCardTextureUid" for the back of the card.

We will also send the optional parameter of cardTextProps. This parameter accepts a CardTextProp object.

A CardTextProp has the following properties:

The following properties are used to adjust the text itself

// Setting text slightly off from the edge of the asset
CompanionCreateObjectEventArgs handCreationEvent = await cardController.CreateCompanionCard(
    "userABC",
    "cardIdA",
    "frontTextureId",
    "backCardTextureUid",
    new CardTextProp
    {
        text = "This is a prompt to show up on the card",
        horizontalPadding = 45
    });

// Setting large red centered text towards the bottom of the card, vertically aligned
CompanionCreateObjectEventArgs handCreationEvent = await cardController.CreateCompanionCard(
    "userABC",
    "cardIdA",
    "frontTextureId",
    "backCardTextureUid",
    new CardTextProp
    {
        text = "This is different text",
        vertialAdjust = 30,
        fontSize = 24,
        font = "Arial",
        color = "#FF0000",
        textAlignment = TextAlignment.Center
    });

Set Card Text

If you want to set the text of an existing card, you can use the SetCardText method on the CardController. This takes the same CardTextProp object, but requires the cardId property to specify the card you want to set the text of.

In this example, we will add text to the card in the users hand with the id "card1".

CompanionMessageResponseArgs response = await cardController.SetCardText("user123", new CardTextProp
    {
        cardId = "card1",
        text = "Setting the card1 text with this value",
        verticalAdjust = 30,
    });

Note: Setting the text will overwrite the previous values, so any properties set before that you want to presist will need to be set again (e.g. color/font/alignment)

Remove Card Text

If you want to remove text from the card, you can call the SetCardText method without sending any text value, this will clear out the text from the cardId specified.

CompanionMessageResponseArgs response = await cardController.SetCardText("user123", new CardTextProp
    {
        cardId = "card1",
    });

You can modify various assets within companion with the SetCardControlAsset method. These are the ControlAssetTypes that can be update with new assets:

    cardController.SetCardControlAsset("user123", ControlAssetType.CardSelectedAssetId, "assetIdForNewSelectedImage");

You can also modify individual card assets as needed with the following:

    cardController.SetCardFrontAssetId("user123", "frontAssetIdToSet");
    cardController.SetCardBackAssetId("user123", "backAssetIdToSet");

You have the ability to highlight specific cards, either with a color or by passing an asset that will be placed on top of the cards.

Each template has it's own look for the highlighting. Below is each template with a green highlight.

Card Template

Step-by-Step

Tile Template

Step-by-Step

Playing Template

Step-by-Step

This example will highlight two cards in the users hand with the id "card1" and "card2"

    cardController.SetCardHighlights("user123", new CardHighlights()
            {
                cardIds = new string[] { "card1", "card2" },
                color = $"#{ColorUtility.ToHtmlStringRGB(Color.green)}",
                enabled = true,
            })

CardHighlights Properties

Users have the ability to drag to reorder cards on the Companion by pressing and holding on a card, and the game has the ability to send messages to reorder those cards as well. You can react to reorder events with CardsReordered. You can see more details in the Card Events sections.

There are two ways to reorder cards.

Say you have sent 3 cards to the companion with the following ids: ["card1", "card2", "card3"].

If you want to reorder to ["card2", "card3", "card1"], you can do it in two different ways.

You can move the first card to the last index:

    //Pass the userId for whose cards you are reordering, the id of the card you want to move, and the index you want the card to end up in
    cardController.ReorderCard("userABC", "card1", 2);

Or you can reorder the whole hand by passing the cardIds in the order you want them to appear (This requires sending all of the cardIds, if the ids don't match what is on Companion, you will receive an error)

    cardController.ReorderHand("userABC", ["card2", "card3", "card1"]);

By default, this will try to reorder the active hand on Companion. If you have more than one hand and you would like to reorder a non-active hand, you can send the optional handId parameter. Imagine you have a hand that has the id "specificHandId".

    cardController.ReorderCard("userABC", "card1", 2, "specificHandId");
    cardController.ReorderHand("userABC", ["card2", "card3", "card1"], "specificHandId");

Although we are working in a managed environment it is always a good idea to clean the listeners when no longer needed.

    public override void _ExitTree()
    {
        cardController.CardPlayed -= OnCardPlayed;
        cardController.CardTapped -= OnCardTapped;
        cardController.CardsReordered -= OnCardsReorderedEvent;
    }

This section include the entire code in one single, easy to copy section.

    CardController cardController;

    // Called when the node enters the scene tree for the first time.
	public override void _Ready()
    {
        var gameboard = GetNode("/root/GameboardSDK") as GameboardPlugin;

        cardController = gameboard.GetNode("CardController") as CardController;

        cardController.CardPlayed += OnCardPlayed;
        cardController.CardTapped += OnCardTapped;
        cardController.CardsReordered += OnCardsReorderedEvent;
    }

    void OnCardPlayed(CompanionCardPlayedEventArgs cardPlayedEvent)
    {
        // Handle Card Played
    }

    void CardTapped(CompanionCardPlayedEventArgs cardTappedEvent) //Same object/info as cardPlayed, except the card was only tapped instead of played
    {
        // Handle Card Tapped
    }

    void OnCardsReorderedEvent(GameboardCardsReorderedEventArgs cardsReorderedEvent)
    {
        //Handler cards reordered
    }

    public override void _ExitTree()
    {
        cardController.CardPlayed -= OnCardPlayed;
        cardController.CardTapped -= OnCardTapped;
        cardController.CardsReordered -= OnCardsReorderedEvent;
    }

Here are some tips and tricks for companion development specifically related to card.

You should have a companion initiation method. Here you can do all the setup work for Companion as users join, and also resend any assets as needed for users that disconnect and reconnect.

Setting Assets

Here you can set items that generally you only want to set once, like the following:

With the following code (changing the ControlAssetType to match what you want to update):

 await cardController.SetCardControlAsset("userABC", ControlAssetType.CardBackgroundAssetId, "backgroundGuid");

Here you can see where the assets are located that you can modify.

Step-by-Step

Here are samples of the assets.

Background: Step-by-Step

Foreground: Step-by-Step

StartControl/EndControl:

Step-by-Step       Step-by-Step

You can see recommended aspect ratios here.

Card Interaction Suggestions

There are many different ways you can use the events from companion to set up the cards in an intuitive way for the users.

For example, if you are allowing the user to select and send multiple cards at once, you can use the OnCardTapped event to keep track of what cards are selected and update the Card Highlight accordingly. And then when the user presses the send cards button, you can remove all of the selected cards.

In this example, we have a Dictionary of playersData that has the userId as the key and a custom PlayerCompanionData object as the value. This object keeps track of the user's hands and their currently active hand. We keep track of the hands and active hand with a custom Hand object that keeps track of the cards in the hand, and the currently selected cards, managed as shown below.

private async void OnCardTapped(CompanionCardPlayedEventArgs cardTappedEvent)
{
    var playerData = playersData[cardTappedEvent.ownerId];

    if (playerData.activeHand.currentlySelectedCards.Contains(cardTappedEvent.cardId))
    {
        playerData.activeHand.currentlySelectedCards.Remove(cardTappedEvent.cardId);
        await cardController.SetCardHighlights(cardTappedEvent.ownerId, new CardHighlights()
        {
            cardIds = new string[] { cardTappedEvent.cardId },
            enabled = false,
        });
    }
    else
    {
        playerData.activeHand.currentlySelectedCards.Add(cardTappedEvent.cardId);
        await cardController.SetCardHighlights(cardTappedEvent.ownerId, new CardHighlights()
        {
            cardIds = new string[] { cardTappedEvent.cardId },
            color = $"#{ColorUtility.ToHtmlStringRGB(Color.green)}",
            enabled = true,
        });
    }
}

Below you can see how you can send the selected cards on button pressed.

private async void OnCardButtonPressed(GameboardCompanionCardsButtonPressedEventArgs companionButtonEvent)
{
    var playerData = playersData[companionButtonEvent.ownerId];
    switch (companionButtonEvent.buttonId)
    {
        case nameof(ButtonId.sendCards):
            //Go through all od the currently highlighted cards and try to remove them
            foreach (var cardId in playerData.activeHand.currentlySelectedCards)
            {
                var response = await cardController.RemoveCardFromHandDisplay(playerData.userId, playerData.activeHand.id, cardId);

                //When successfully removed from companion, update local data
                if (response.wasSuccessful)
                {
                    playerData.activeHand.cards.Remove(cardId);
                }
            }
            break;
    }
}